////////////////////////////////////////////////////////////////////////////////
//
//  The MIT License
//
//  Copyright (c) 2008 - 2009. Dimarik
//
//  Permission is hereby granted, free of charge, to any person obtaining a copy
//  of this software and associated documentation files (the "Software"), to deal
//  in the Software without restriction, including without limitation the rights
//  to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
//  copies of the Software, and to permit persons to whom the Software is
//  furnished to do so, subject to the following conditions:
//
//  The above copyright notice and this permission notice shall be included in
//  all copies or substantial portions of the Software.
//
//  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
//  IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
//  FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
//  AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
//  LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
//  OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
//  THE SOFTWARE.
//
////////////////////////////////////////////////////////////////////////////////

package ru.goodcore.transport
{

import flash.events.Event;
import flash.events.HTTPStatusEvent;
import flash.events.IOErrorEvent;
import flash.events.ProgressEvent;
import flash.events.SecurityErrorEvent;
import flash.events.StatusEvent;
import flash.net.URLLoader;
import flash.net.URLLoaderDataFormat;
import flash.net.URLRequest;
import flash.net.URLRequestMethod;
import flash.utils.getClassName;

import ru.goodcore.core.QueryEvent;
import ru.goodcore.logging.ILogger;
import ru.goodcore.logging.Log;
import ru.goodcore.net.URI;

/**
 *  @author				Dimarik
 *  @version			1.0
 *  @langversion		3.0
 *  @playerversion		9.0
 *
 */
public class HTTPTransport extends AbstractTransport {

	//-------------------------------------------------------------------------
	//
	//   Private class's variables
	//
	//-------------------------------------------------------------------------

	//-------------------------------------------------------------------------
	//
	//   Class's methods
	//
	//-------------------------------------------------------------------------

	//-------------------------------------------------------------------------
	//
	//   Constructor
	//
	//-------------------------------------------------------------------------

	public function HTTPTransport() {
		super();
		super.name = getClassName(this);
		this.loader.dataFormat = URLLoaderDataFormat.TEXT;
	}

	//-------------------------------------------------------------------------
	//
	//   Overriden properties: AbstractTransport
	//
	//-------------------------------------------------------------------------

	public override function get logger():ILogger {
		if ( !super.$logger ) {
			this.$logger = Log.getLogger('HTTPTransport');
		}
		return this.$logger;
	}

	//-------------------------------------------------------------------------
	//
	//   Properties
	//
	//-------------------------------------------------------------------------

	//---------------------------------
	//   url
	//---------------------------------
	private var _url:	String;

	public function get url():String {
		return this._url;
	}

	public function set url(value:String):void {
		if (this._url == value) return;
		this._url = value;
	}

	//---------------------------------
	//   method
	//---------------------------------
	private var _method:	String = URLRequestMethod.POST;

	public function get method():String {
		return this._method;
	}

	public function set method(value:String):void {
		if (this._method == value) return;
		this._method = value;
	}

	//---------------------------------
	//   dataFormat
	//---------------------------------
	private var _dataFormat:	String;

	public function get dataFormat():String {
		return this.loader.dataFormat;
	}

	public function set dataFormat(value:String):void {
		if (this.loader.dataFormat == value) return;
		this.loader.dataFormat = value;
	}

	//---------------------------------
	//   httpStatus
	//---------------------------------
	private var _httpStatus:	int;

	public function get httpStatus():int {
		return this._httpStatus;
	}

	//-------------------------------------------------------------------------
	//
	//   Protected variables
	//
	//-------------------------------------------------------------------------

	protected var loader:			URLLoader = new URLLoader();

	protected var currentRequest:	Object;

	//-------------------------------------------------------------------------
	//
	//   Private constants
	//
	//-------------------------------------------------------------------------

	private const _queue:			Array = new Array();

	//-------------------------------------------------------------------------
	//
	//   Private variables
	//
	//-------------------------------------------------------------------------

	/**
	 * @private 
	 */
	private var _urlRequest:		URLRequest;

	//-------------------------------------------------------------------------
	//
	//   Public methods
	//
	//-------------------------------------------------------------------------

	public override function perfomQuery(request:Object):void {
		if ( !super.connected ) {
			this.connect(request.uri);
		} else {
			this.send(request);
		}
	}

	public override function connect(uri:URI):void {
		this.clear();

		// Just check is destination available
		var url:String = uri.toDisplayString();
		this._urlRequest = new URLRequest(url);
		this.loader.load(this._urlRequest);

		this.loader.addEventListener(Event.COMPLETE, 					this.handler_successConnect);
		this.loader.addEventListener(IOErrorEvent.IO_ERROR,				this.handler_faultConnect);
		this.loader.addEventListener(SecurityErrorEvent.SECURITY_ERROR,	this.handler_faultConnect);
		this.loader.addEventListener(HTTPStatusEvent.HTTP_STATUS,		this.handler_httpStatusConnect);
	}


	public override function send(options:Object = null):void {
		var requestObject:Object = new Object();
		requestObject.options = options;

		this._queue.push(requestObject);

		if( !this.currentRequest ) this.requestNext();
	}


	public override function close():void {
		super.connected = false;

		try {
			this.loader.close();
		} catch (error:Error) {
			// ignore
		}

	}

	//---------------------------------------------------------------------
	//
	//   Protected methods
	//
	//---------------------------------------------------------------------

	/**
	 * @private
	 */
	protected function requestNext():void {
		if (this._queue.length == 0) return;

		this.currentRequest = this._queue.shift();

		var options:Object = this.currentRequest.options;

		if (options) {
			this._urlRequest.data = options;
			this._urlRequest.method = URLRequestMethod.POST;
		} else {
			this._urlRequest.data = null;
			this._urlRequest.method = URLRequestMethod.GET;
		}

		this.loader.load(this._urlRequest);
	}

	//---------------------------------------------------------------------
	//
	//   Private methods
	//
	//---------------------------------------------------------------------

	/**
	 * @private
	 */
	private function clear():void {
		// Clear queue
		this._queue.splice(0, this._queue.length);
		this.currentRequest = null;
	}

	//---------------------------------------------------------------------
	//
	//   Event handlers
	//
	//---------------------------------------------------------------------

	/**
	 * @private
	 * Connect successfull!
	 */
	protected function handler_successConnect(event:Event):void {
		this.loader.removeEventListener(Event.COMPLETE, 					this.handler_successConnect);
		this.loader.removeEventListener(IOErrorEvent.IO_ERROR,				this.handler_faultConnect);
		this.loader.removeEventListener(SecurityErrorEvent.SECURITY_ERROR,	this.handler_faultConnect);
		this.loader.removeEventListener(HTTPStatusEvent.HTTP_STATUS,		this.handler_httpStatusConnect);

		this.loader.addEventListener(Event.COMPLETE, 					this.handler_success);
		this.loader.addEventListener(IOErrorEvent.IO_ERROR,				this.handler_fault);
		this.loader.addEventListener(SecurityErrorEvent.SECURITY_ERROR,	this.handler_fault);
		this.loader.addEventListener(HTTPStatusEvent.HTTP_STATUS,		this.handler_httpStatus);

		super.connected = true;

		super.dispatchEvent( new Event(Event.CONNECT) );
		super.dispatchEvent( new QueryEvent(this.loader.data) );
	}

	/**
	 * @private
	 */
	protected function handler_faultConnect(event:Event):void {
		this.loader.removeEventListener(Event.COMPLETE, 					this.handler_successConnect);
		this.loader.removeEventListener(IOErrorEvent.IO_ERROR,				this.handler_faultConnect);
		this.loader.removeEventListener(SecurityErrorEvent.SECURITY_ERROR,	this.handler_faultConnect);
		this.loader.removeEventListener(HTTPStatusEvent.HTTP_STATUS,		this.handler_httpStatusConnect);

		super.dispatchEvent( event );
	}

	/**
	 * @private
	 */
	protected function handler_httpStatusConnect(event:HTTPStatusEvent):void {
		super.dispatchEvent( new Event(StatusEvent.STATUS) );
	}

	/**
	 * @private
	 */
	protected function handler_success(event:Event):void {
		super.data = (event.target as URLLoader).data;
		super.dispatchEvent( new ProgressEvent(ProgressEvent.SOCKET_DATA) );
		super.dispatchEvent( new QueryEvent(super.data) );
		this.currentRequest = null;
		this.requestNext();
	}

	/**
	 * @private
	 */
	protected function handler_fault(event:Event):void {
		this.loader.removeEventListener(Event.COMPLETE, 					this.handler_success);
		this.loader.removeEventListener(IOErrorEvent.IO_ERROR,				this.handler_fault);
		this.loader.removeEventListener(SecurityErrorEvent.SECURITY_ERROR,	this.handler_fault);
		this.loader.removeEventListener(HTTPStatusEvent.HTTP_STATUS,		this.handler_httpStatus);

		super.connected = false;

		super.dispatchEvent(new Event(Event.CLOSE) );
	}

	/**
	 * @private
	 */
	protected function handler_httpStatus(event:HTTPStatusEvent):void {
		super.dispatchEvent( new Event(StatusEvent.STATUS) );
		this._httpStatus = event.status;
	}

}
}